令人迷惑的写法
-
下面的程序想要表达什么意思?
template<class T>
class Test {
public:
Test(T t) { }
};
template<class T>
void func(T a[ ],int len) {
} -
历史上的原因
- 早期的C++直接复用class关键字来定义模板
- 但是泛型编程针对的不只是类类型
- class关键字的复用使得代码出现二义性
-
typename诞生的直接诱因
- 自定义类类型内部的嵌套类型
- 不同类中的同一个标识符可能导致二义性
- 编译器无法辨识标识符究竟是什么?
编程实验
-
模板中的二义性
#include <iostream>
#include <string>
using namespace std;
template < class T >
class Test {
public:
Test(T t) {
cout << "t = " << t << endl;
}
};
template < class T >
void func(T a[], int len) {
for(int i=0; i<len; i++) {
cout << a[i] << endl;
}
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
int a = 0;
class Test_1 {
public:
static const int TS = 1;
};
class Test_2 {
public:
struct TS {
int value;
};
};
template
< class T >
void test_class() {
typename T::TS * a; // 1. 通过泛指类型 T 内部的数据类型 TS
//定义指针变量 a (推荐的解读方式)
// 2. 使用泛指类型 T 内部的静态成员变量 TS
//与全局变量 a 进行乘法操作
}
int main(int argc, char *argv[]) {
// test_class<Test_1>();
test_class<Test_2>();
return 0;
}
令人迷惑的写法
typename的作用:
- 在模板定义中声明泛指类型
- 明确告诉编译器其后的标识符为类型
-
下面的程序想要表达什么意思?
int func(int i) try {
return i;
} catch(...) {
return -1;
}
int func(int i,int j) throw(int) {
return i+j;
} -
try...catch用于分隔正常功能代码与异常处理代码
-
try...catch可以直接将函数实现分隔为2部分
-
函数声明和定义时可以直接指定可能抛出的异常类型
-
异常声明成为函数的一部分可以提高代码可读性
-
函数异常声明的注意事项
- 函数异常声明是一种与编译器之间的契约
- 函数声明异常后就只能抛出声明的异常
- 抛出其它异常将导致程序运行终止
- 可以直接通过异常声明定义无异常函数
编程实验
-
新的异常算法
#include <iostream>
#include <string>
using namespace std;
int func(int i, int j) throw(int, char) {
if( (0 < j) && (j < 10) ) {
return (i + j);
} else {
throw '0';
}
}
void test(int i) try {
cout << "func(i, i) = " << func(i, i) << endl;
} catch(int i) {
cout << "Exception: " << i << endl;
} catch(...) {
cout << "Exception..." << endl;
}
int main(int argc, char *argv[]) {
test(5);
test(10);
return 0;
}
小结
- class可以用来在模板中定义泛指类型(不推荐)
- typename是可以消除模板中的二义性
- try...catch可以将函数体分成2部分
- 异常声明能够提供程序的可读性